//
// Created by wujehy on 2020/8/4.
//

#include <iostream>
#include "AuthMod.h"
#include "Network.pb.h"
#include "../NetWorkWarpper.h"
#include <handy/handy.h>
#include <sstream>
#include <NetWorkModule/ContextDataType.h>
#include <Tool/GeeJoanMD5.h>
#include "GeeJoan/AuthModule.pb.h"
#include "../Context/TaskContext.h"
#include "../Dao/UserDB.h"
#include "Tool/Common.h"

using handy::Logger;
using namespace GeeJoan::Common;

int AuthMod::type()
{
    return Type::Mod_Auth;
}

int AuthMod::depackage(int taskid, int type_, void *data, int len, void *userdata)
{
    auto find = m_type_event_map.find( type_ );
    if (find != m_type_event_map.end())
    {
        return find->second( taskid, data, len, userdata );
    } else
    {
        return -2;
    }

//    std::cout <<" get data " <<std::endl;
//
//    GetMasterRequest getMasterRequest;
//    auto taskContext = businessManager->getContextManager()->get_unique_ptr<TaskContext<TaskDataType>>(taskid, 1);
//    if (!getMasterRequest.ParseFromArray(data , len))
//    {
//        std::cout <<" error " <<std::endl;
//        return 0;
//    }
//    std::cout <<" data : " << getMasterRequest.key() <<std::endl;
//
//    if (type ==1 )
//    {
//        auto find= context->m_id_client_map.find(taskContext->context.tcpid);
//        if (find != context->m_id_client_map.end())
//        {
//            find->second->send("hello0000");
//        }
//    }
    return 0;
}


int AuthMod::init_event()
{

    m_type_event_map.insert( {AUTH_REGISTER, register_event_function} );
    m_type_event_map.insert( {AUTH_LOGIN, login_event_function} );
    m_type_event_map.insert( {AUTH_LOGOUT, logOut_event_function} );
    m_type_event_map.insert( {AUTH_CHANGE_PASSWORD, changePassword_event_function} );

    return 0;
}

// TODO 生成 token 的方法 Test
static std::string genLoginToken(std::string &user, std::string &password)
{
    std::stringstream ss;
    ss << "Token." << user << "." << password << "." << handy::util::timeMilli();
    return StringToHex( ss.str());
}

void AuthMod::init_callback_function()
{

    using namespace GeeJoan::Protocol::Auth;
//
    register_event_function = [&](int64_t taskid, void *data, int len, void *userdata)
    {
        info( "doing register " );

        RegisterAPI registerApi;
        if (!registerApi.ParseFromArray( data, len ))
        {
            // 失败
            businessManager->call500( taskid, "error" );
            return -1;
        }

        info( "registerApi test , %s ", registerApi.username().c_str());
        std::string username = registerApi.username();
        std::string srcPassword = registerApi.password();
        std::string password = GeeJoanMD5( srcPassword ).toString();
        std::string email = registerApi.email();

        if (username.empty() || srcPassword.empty() || email.empty())
        {
            businessManager->call500( taskid, "param error " );
            return 0;
        }

//        auto userTable = businessManager->getDaoManager()->getTable<UserDB>(1);
        int ret = userTable->addUser( username, password, email );
        std::string outstring;
        if (ret == SUCCESS)
        {
            outstring = "";
            businessManager->call200( taskid, outstring );
        } else
        {
            businessManager->call500( taskid, "errmsg" );
        }
        return 0;
    };


    login_event_function = [&](int64_t taskid, void *data, int len, void *userdata)
    {
        info( "doing login " );

        LoginRequest loginRequest;

        if (!loginRequest.ParseFromArray( data, len ))
        {
            // 失败
            businessManager->call500( taskid, "error" );
            return 0;
        }

        std::string username = loginRequest.username();

        std::string srcPassword = loginRequest.password();
        std::string password = GeeJoanMD5( srcPassword ).toString();

        if (username.empty() || password.empty())
        {
            // 失败
            businessManager->call500( taskid, "param error" );
            return 0;
        }

        int ret = userTable->checktUser( username, password );
        std::string outstring;
        if (ret == SUCCESS)
        {
            int64_t uid = userTable->getUidByUsername( username );
            outstring = "";
            LoginRespose loginRespose;

            auto taskdata = businessManager->getContextManager()->get<TaskContext>( taskid, 1 );
            std::string token = genLoginToken( username, password );
            auto conn = businessManager->getNetWorkWarpper()->m_id_client_map[taskdata->tcpid];
            conn->context<ContextDataType>().username = username;
            conn->context<ContextDataType>().tokenstr = token;
            conn->context<ContextDataType>().status = 1;
            conn->context<ContextDataType>().uid = uid;
            loginRespose.set_token( token );
            loginRespose.SerializePartialToString( &outstring );
            businessManager->call200( taskid, outstring );
        } else
        {
            businessManager->call500( taskid, "errmsg" );
            return -1;
        }

        return -2;
    };

    logOut_event_function = [&](int64_t taskid, void *data, int len, void *userdata)
    {
        info( "doing logout " );
        LogoutAPI logoutApi;
        if (!logoutApi.ParseFromArray( data, len ))
        {
            businessManager->call500( taskid, "errmsg" );
            return -1;
        }
        // 成功
        auto taskdata = businessManager->getContextManager()->get<TaskContext>( taskid, 1 );
        auto conn = businessManager->getNetWorkWarpper()->m_id_client_map[taskdata->tcpid];

        std::string userToken = logoutApi.token();
        std::string serverToken = conn->context<ContextDataType>().tokenstr;
//        int64_t userTime = logoutApi.timestamp();
        if (userToken == serverToken)
        {
            conn->context<ContextDataType>().tokenstr = "";
            conn->context<ContextDataType>().username = "";
            conn->context<ContextDataType>().uid = 0;
            std::string outstring = "";
            businessManager->call200( taskid, outstring );
        } else
        {
            businessManager->call500( taskid, " not found your token or timeout! " );
            return -1;
        }
        return -2;

    };

    changePassword_event_function = [&](int64_t taskid, void *data, int len, void *userdata)
    {
        auto taskdata = businessManager->getContextManager()->get<TaskContext>( taskid, 1 );
        auto conn = businessManager->getNetWorkWarpper()->m_id_client_map[taskdata->tcpid];


        bool islogin = (conn->context<ContextDataType>().status == 1);

        if (islogin)
        {
            businessManager->call500( taskid, " your not login " );
            return -1;
        }

        ChangePasswordRequest changePasswordRequest;
        if (!changePasswordRequest.ParseFromArray( data, len ))
        {
            // 解包失败
            businessManager->call500( taskid, "error" );
            return -1;
        }
        // 成功

        std::string serverToken = conn->context<ContextDataType>().tokenstr;
        std::string userToken = changePasswordRequest.oldtoken();
        //TODO
        std::string newpassword = GeeJoanMD5( changePasswordRequest.newpassword()).toString();
        bool isok = (userToken == serverToken);
        if (!isok)
        {
            businessManager->call500( taskid, " not found your token or timeout! " );
            return -1;
        }

        // 参数正确
        std::string username = conn->context<ContextDataType>().username;
//        auto userTable = businessManager->getDaoManager()->getTable<UserDB>(1);
        int ret = userTable->changePassword( username, newpassword );

        if (ret == SUCCESS)
        {
            ChangePasswordRespose changePasswordRespose;
            std::string newToken = genLoginToken( username, newpassword );
            changePasswordRespose.set_newtoken( newToken );
            conn->context<ContextDataType>().tokenstr = newToken;
            std::string outstring;
            changePasswordRespose.SerializeToString( &outstring );
            businessManager->call200( taskid, outstring );

        } else
        {
            businessManager->call500( taskid, "change fail" );
            return -1;
        }
        return -2;

    };


}

void AuthMod::setBusinessManager(BusinessManager *businessManager)
{
    AuthMod::businessManager = businessManager;
}

AuthMod::AuthMod()
{
    init_callback_function();

    init_event();
}

void AuthMod::init_finished()
{

    info( " AuthMod::init_finished " );
    userTable = businessManager->getDaoManager()->getTable<UserDB>( 1 );
}
